package com.ptkc.utils;

import com.alibaba.fastjson.JSON;
import lombok.extern.log4j.Log4j;
import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.proto.ExistsRequest;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.VersionType;
import org.elasticsearch.rest.RestStatus;
import org.springframework.cglib.beans.BeanMap;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @author: Mr.Jiao
 * @date: 2018年12月06日
 */
@Slf4j
public class ESUtil {

    private RestHighLevelClient restHighLevelClient;

    public ESUtil(RestHighLevelClient client){
        this.restHighLevelClient = client;
    }

    public boolean updateSelective(String index, String type, String id, Object value) throws IOException {
        GetRequest getRequest = new GetRequest(index, type, id);
        if(this.restHighLevelClient.exists(getRequest)) {
            UpdateRequest request = new UpdateRequest(index, type, id);
            request.doc(this.getBeanMap(value, true));
            UpdateResponse response = this.restHighLevelClient.update(request);
            if(response.status() == RestStatus.OK){
                return true;
            }
        }
        return false;
    }

    public DocWriteResponse save(String index, String type, String id, Object value) throws IOException {
        Map<String, Object> beanMap = null;
        if(value instanceof Map){
            beanMap = new HashMap<>((Map)value);
        }else{
            beanMap = new HashMap(BeanMap.create(value));
        }
        IndexRequest request = new IndexRequest(index, type, id);
        request.source(beanMap).versionType(VersionType.EXTERNAL);
        UpdateRequest updateRequest = new UpdateRequest(index, type, id);
        Map<String, Object> existsValueMap = this.getBeanMap(value, false);
        XContentBuilder xContentBuilder = XContentFactory.jsonBuilder().startObject();
        Set<Map.Entry<String, Object>> existsSets = existsValueMap.entrySet();
        for(Map.Entry<String, Object> entry : existsSets){
            xContentBuilder.field(entry.getKey(), entry.getValue());
        }
        xContentBuilder.endObject();
        updateRequest.doc(xContentBuilder).upsert(request);
        updateRequest.fetchSource(existsValueMap.keySet().toArray(new String[]{}), null);
        return restHighLevelClient.update(updateRequest);
    }

    private Map<String,Object> getBeanMap(Object value, boolean isRemoveNull){
        Map<String, Object> beanMap = new HashMap(BeanMap.create(value));
        List<Field> fields = this.getFields(value.getClass());
        for(Field field : fields){
            boolean isNull = beanMap.get(field.getName()) == null;
            if(isRemoveNull) {
                if(isNull){
                    beanMap.remove(field.getName());
                }
            }else{
                IgnoreIfNull ignoreIfNull = field.getAnnotation(IgnoreIfNull.class);
                if (ignoreIfNull != null) {
                    if (isNull) {
                        beanMap.remove(field.getName());
                    }
                }
            }
        }
        return beanMap;
    }

    private List<Field> getFields(Class clazz){
        Field []declaredFields = clazz.getDeclaredFields();
        List<Field> fields = new ArrayList<>();
        if(declaredFields != null){
            fields.addAll(Arrays.asList(declaredFields));
        }
        Class<?> superClass = clazz.getSuperclass();
        if(superClass != null) {
            List<Field> supporFields = getFields(superClass);
            fields.addAll(supporFields);
        }
        return fields;
    }

}
